/*

     This file is part of glstart.

    glstart is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

    glstart is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along with Foobar. If not, see <https://www.gnu.org/licenses/>.
*/


#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <X11/X.h>
#include <X11/keysymdef.h>
#include <X11/Xlib.h>

#define GL_GLEXT_PROTOTYPES

#include <GL/gl.h>
#include <GL/glx.h>
#include <GL/glu.h>
#include <GL/glext.h>
#include <GL/glcorearb.h>
#include "vertexes.h"
#include "shaders.h"
#include "textures.h"
#include "obj3d.h"
#include "objmtl.h"
#include "marray.h"

Display* dpy;
Window root;
GLint att[] = {GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None};
XVisualInfo* vi;
Colormap cmap;
XSetWindowAttributes swa;
Window win;
GLXContext glc;
XWindowAttributes gwa;
XEvent xev;

int main(int argc, char* argv[]) {

	if(argc != 5) {
		printf("syntax: %s <vertex shader file> <fragment shader file> <bmp file> <obj file>\n", argv[0]);
		return 0;
	}

	dpy = XOpenDisplay(NULL);
	if(dpy == NULL) {
		printf("\n\tCannot connect to X server\n\n");
		exit(0);
	};

	root = DefaultRootWindow(dpy);
	vi = glXChooseVisual(dpy, 0, att);
	if(vi == NULL) {
		printf("\n\tno appropriate visual found\n\n");
		exit(0);
	} else {
		printf("\n\tvisual %p selected\n", (void*)vi->visualid);
	};

	cmap = XCreateColormap(dpy, root, vi->visual, AllocNone);
	swa.colormap = cmap;
	swa.event_mask = KeyPressMask | PointerMotionMask;

	win = XCreateWindow(dpy, root, 0, 0, 600, 600, 0, vi->depth, InputOutput, vi->visual, CWColormap | CWEventMask, &swa);
	XMapWindow(dpy, win);
	XStoreName(dpy, win, "OpenGL Mouse Look");

	glc = glXCreateContext(dpy, vi, NULL, GL_TRUE);
	glXMakeCurrent(dpy, win, glc);

	glEnable(GL_DEPTH_TEST);


	Obj3D o3d;

	OBJFILE* ofile = readOBJFILE(argv[4]);
	MArray* hvbo = makeVBO(ofile);

	float verts[] = { 0.0f, 0.9f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, -1.0f,
                    0.9f, -0.9f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, -1.0f,
                    -0.9f, -0.9f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, -1.0f,
                  };

	obj3dInit(&o3d, CreateShaderProgram(argv[1], argv[2]),
 									CreateVAO(hvbo->d, hvbo->size, NULL, 0),
									hvbo->size/(sizeof(float)*8), loadBmpTexture(argv[3]));

	freeMArray(hvbo);
	freeOBJFILE(ofile);

	float n = 0.05f;
	float f = 1000.0f;
	float l = 0.05f;
	float t = 0.05f;
	float cx = 0.0f;
	float cz = 0.0f;
	float angleh = 0.0f;
	float anglev = 0.0f;


	float pers[] = {
		2*n/l, 0.0f, 0.0f, 0.0f,
		0.0f, 2*n/t, 0.0f, 0.0f,
		0.0f, 0.0f, (n+f)/(n-f), 2*n*f/(n-f),
		0.0f, 0.0f, -1.0f, 0.0f,
	};

	float view[] = {
		1.0f, 0.0f, 0.0f, 0.0f,
		0.0f, 1.0f, 0.0f, 0.0f,
		0.0f, 0.0f, 1.0f, 0.0f,
		0.0f, 0.0f, 0.0f, 1.0f
	};

	//XGrabPointer(dpy, win, True, PointerMotionMask, GrabModeAsync, GrabModeAsync, win, None, CurrentTime);

	while(1) {
		if(XCheckTypedWindowEvent(dpy, win, KeyPress, &xev)) {
			KeySym sym = XLookupKeysym(&xev.xkey, 0);
      switch(sym) {
        case XK_Escape:
					glXMakeCurrent(dpy, None, NULL);
					glXDestroyContext(dpy, glc);
					XDestroyWindow(dpy, win);
					XCloseDisplay(dpy);
					exit(0);
					break;
				case XK_w:
					cz += -0.1f*cos(angleh);
					cx += -0.1f*sin(angleh);
					break;
				case XK_s:
					cz += 0.1f*cos(angleh);
					cx += 0.1f*sin(angleh);
					break;
				case XK_a:
					cx += -0.1f;
					break;
				case XK_d:
					cx += 0.1f;
					break;
				case XK_q:
					angleh += 0.01f;
					break;
				case XK_e:
					angleh += -0.01f;
					break;
				case XK_z:
					anglev += 0.01f;
					break;
				case XK_c:
					anglev += -0.01f;
					break;

        default:
					break;
      };
    };

		view[11] = -cz;
		view[3] = -cx;

		if(angleh > 3.14f) angleh -= 6.28f;
		if(angleh < -3.14f) angleh += 6.28f;

		if(anglev > 3.14f*0.5f) anglev = 3.14f*0.5f;
		if(anglev < -3.14f*0.5f) anglev = -3.14f*0.5f;

		float roth[] = {
			cos(-angleh), 0.0f, sin(-angleh), 0.0f,
			0.0f, 1.0f, 0.0f, 0.0f,
			-sin(-angleh), 0.0f, cos(-angleh), 0.0f,
			0.0f, 0.0f, 0.0f, 1.0f
		};

		float rotv[] = {
			1.0f, 0.0f, 0.0f, 0.0f,
			0.0f, cos(-anglev), -sin(-anglev), 0.0f,
			0.0f, sin(-anglev), cos(-anglev), 0.0f,
			0.0f, 0.0f, 0.0f, 1.0f
		};


		XGetWindowAttributes(dpy, win, &gwa);

		short mx = gwa.width/2;
		short my = gwa.height/2;
		int dx = 0;
		int dy = 0;

		Window r_w, c_w;
		int root_x_return, root_y_return;
		unsigned int mask_return;
		if(True == XQueryPointer(dpy, win, &r_w, &c_w, &root_x_return, &root_y_return, &dx, &dy, &mask_return)) {
			dx = mx - dx;
			dy = my - dy;
			float dangle_xz = ((float)dx)/1000.0f;
			float dangle_yz = ((float)dy)/1000.0f;
			angleh += dangle_xz;
			anglev += dangle_yz;
		};

		XWarpPointer(dpy, win, win, 0, 0, gwa.width, gwa.height, gwa.width/2, gwa.height/2);

		glViewport(0, 0, gwa.width, gwa.height);

		glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

		glUseProgram(o3d.shaderProgram);
		glUniform1i(glGetUniformLocation(o3d.shaderProgram, "tex1"), 0);
		glUniformMatrix4fv(glGetUniformLocation(o3d.shaderProgram, "roth"), 1, GL_TRUE, roth);
		glUniformMatrix4fv(glGetUniformLocation(o3d.shaderProgram, "rotv"), 1, GL_TRUE, rotv);
		glUniformMatrix4fv(glGetUniformLocation(o3d.shaderProgram, "pers"), 1, GL_TRUE, pers);
		glUniformMatrix4fv(glGetUniformLocation(o3d.shaderProgram, "view"), 1, GL_TRUE, view);
		glUniformMatrix4fv(glGetUniformLocation(o3d.shaderProgram, "trans"), 1, GL_TRUE, o3d.transform);
		glActiveTexture(GL_TEXTURE0);
		glBindTexture(GL_TEXTURE_2D, o3d.tex);

		glBindVertexArray(o3d.VAO);
		glDrawArrays(GL_TRIANGLES, 0, o3d.vao_size);

		glXSwapBuffers(dpy, win);
	};
};
